﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NUnit.Framework;
using DomainDriver.DomainModeling.DomainObjects;

namespace DomainDriver.DomainModeling.GenericUnitTesting
{
    public delegate void DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT>(KEYED_DOMAIN_OBJECT domainObject)
        where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>;

    public abstract class DomainObjectTestFixture<KEY, KEYED_DOMAIN_OBJECT>
        where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
    {
        public static readonly DateTime m_DateTimeUpdateValue = new DateTime(2001, 1, 1);
        public static readonly DateTime m_DateTimeUpdateValue2 = new DateTime(2002, 1, 1);
        public static readonly DateTime m_DateTimeUpdateValue3 = new DateTime(2003, 1, 1);
        public static readonly double m_DoubleUpdateValue = 1.5;
        public static readonly double m_DoubleUpdateValue2 = 3;
        public static readonly double m_DoubleUpdateValue3 = 4.5;
        public static readonly Guid m_GuidUpdateValue = Guid.NewGuid();
        public static readonly Guid m_GuidUpdateValue2 = Guid.NewGuid();
        public static readonly Guid m_GuidUpdateValue3 = Guid.NewGuid();
        public static readonly int m_IntUpdateValue = 1;
        public static readonly int m_IntUpdateValue2 = 2;
        public static readonly int m_IntUpdateValue3 = 3;
        public static readonly string m_StringUpdateValue = " UPDATED";

        protected abstract KEYED_DOMAIN_OBJECT CreateUniqueDomainObject();
        protected abstract ICollection<DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT>> GetDomainObjectModifiers();

        public ICollection<DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT>> Modifiers
        {
            get { return GetDomainObjectModifiers(); }
        }

        public DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT> GetModifier(int modifierIndex)
        {
            return Modifiers.ElementAt(modifierIndex);
        }

        public void ApplyModifier(KEYED_DOMAIN_OBJECT objectToModify, int modifierIndex)
        {
            DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT> modifier = GetModifier(modifierIndex);
            modifier(objectToModify);
        }

        [Test]
        public virtual void VerifyDomainObjectFunctionality()
        {
            ICollection<DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT>> modifiers = GetDomainObjectModifiers();
            KEYED_DOMAIN_OBJECT first = CreateUniqueDomainObject();
            KEYED_DOMAIN_OBJECT second = CreateUniqueDomainObject();
            KEYED_DOMAIN_OBJECT copy = CreateUniqueDomainObject();

            AssertAreNotSame(first, second, copy);
            Assert.AreNotEqual(first, second);
            Assert.AreNotEqual(first, copy);
            Assert.AreNotEqual(second, copy);

            second = first.Copy();
            copy = first.Copy();

            AssertAreNotSame(first, second, copy);
            Assert.AreEqual(first, second);
            Assert.AreEqual(first, copy);
            Assert.AreEqual(second, copy);

            foreach (DomainObjectModifier<KEY, KEYED_DOMAIN_OBJECT> modifier in modifiers)
            {
                modifier(first);

                AssertAreNotSame(first, second, copy);
                Assert.AreNotEqual(first, second);
                Assert.AreNotEqual(first, copy);
                Assert.AreEqual(second, copy);

                modifier(second);

                AssertAreNotSame(first, second, copy);
                Assert.AreEqual(first, second);
                Assert.AreNotEqual(first, copy);
                Assert.AreNotEqual(second, copy);

                copy = first.Copy();

                AssertAreNotSame(first, second, copy);
                Assert.AreEqual(first, second);
                Assert.AreEqual(first, copy);
                Assert.AreEqual(second, copy);
            }
        }

        private static void AssertAreNotSame(KEYED_DOMAIN_OBJECT first, KEYED_DOMAIN_OBJECT second, KEYED_DOMAIN_OBJECT copy)
        {
            Assert.AreNotSame(first, second);
            Assert.AreNotSame(first, copy);
            Assert.AreNotSame(second, copy);
        }
    }
}